var REDUCE_SPACE = true; // turn off if doing stuff like <pre>'s where
                         // multple spaces in a row is not equivilant
                         // to just a single space

var writeln;
var indent = 0;

function getIndent() {
    return indent;
}

function html2js() {

    var pp = XML.prettyPrinting;
    var iw = XML.ignoreWhitespace;
    var ipi = XML.ignoreProcessingInstructions;
    var ic = XML.ignoreComments;

    XML.prettyPrinting = false;
    XML.ignoreWhitespace = false;
    XML.ignoreProcessingInstructions = true;
    XML.ignoreComments = true;

    var xml = XML(trim(document.body.innerHTML));
    xml.normalize();

    var outputWin = window.open();
    outputWin.document.write('');
    outputWin.document.close();

    var pre = outputWin.document.createElement('pre');

    outputWin.document.body.appendChild(pre);

    writeln = function(arg) {

        arg = l2s(arg);

        var dent = [];
        for (i = 0; i < getIndent(); i += 1)
            dent[dent.length] = '    ';
        dent = dent.join('');

        pre.appendChild(outputWin.document.createTextNode(dent + arg + '\n'));

    }

    var id = handleNode(xml);

    statement(['document.body.appendChild(', id, ')']);

    XML.prettyPrinting = pp;
    XML.ignoreWhitespace = iw;
    XML.ignoreProcessingInstructions = ipi;
    XML.ignoreComments = ic;

}

function declaration(lhs, rhs) {

    var lhsWithVar = ['var '].concat(lhs);

    if (typeof rhs == 'undefined')
        statement(lhsWithVar);
    else
        assignment(lhsWithVar, rhs);

}

function assignment(lhs, rhs) {
    statement([ l2s(lhs), ' = ', l2s(rhs)]);
}

function statement(stmt) {
    writeln(l2s(stmt) + ';');
}

//function propAssgmt(elem, props, rhs) {
//    assignment([elem].concat(props).join('.'), rhs);
//}

function startWith(name) {
    writeln(['with (', name, ') {\n']);
    indent++;
}

function endWith() {
    indent--;
    writeln('');
    writeln('}\n');
}

function l2s(arg) {

    if (typeof arg == 'object' && arg.length)
        return arg.join('');
    else
        return arg.toString();

}

var anonNodeNums = {};

function handleNode(node) {

    var nodeName = node.name();
    if (nodeName == null) {

        // handle text node
        // NOTE - per the E4X spec, this could actually also be a XML comment
        // worry about this later...

        var outputText = true;
        if (trim(node.toString()).length == 0) {


            // remove white space between div's.
            // normalize takes care of most if not all of this though...
            // also, might want to generalize this to handle more than just
            // div's
            var myIndex = node.childIndex();
            if (myIndex > 0 && myIndex < node.parent().children().length() - 1){

                if (node.parent().children()[myIndex - 1].localName() == 'div'
                    &&
                    node.parent().children()[myIndex + 1].localName() == 'div'){
                    outputText = false;
                }

            }
        }

        if (outputText) {

            var text = node.toString();

            if (REDUCE_SPACE)
                text = text.replace(/\s+/g, ' ');

            statement(['appendChild(document.createTextNode(',q(text), '))']);

        }

        return null;

    }


    // handle element node
    // NOTE - per the E4X spec, this could actually also be a XML PI
    // worry about this later...

    var tagName = node.localName();

    var id = node.@id.toString();
    if (id.length == 0) {

        if (!anonNodeNums[tagName])
            anonNodeNums[tagName] = 0;

        id = tagName + anonNodeNums[tagName]++;

    }

    declaration(id, ['document.createElement(', q(tagName), ')']);

    if (node.attributes().length() || node.children().length()) {

        startWith(id);

        for each (attr in node.attributes()) {
            var attrName = attr.name();
            if (attrName == 'style') {
                handleStyle(attr.toString());
            }
            else {
                assignment(attr.name(), q(attr.toString()));
            }
        }

        for each (child in node.children()) {
            var childId = handleNode(child);
            if (childId != null)
                statement(['appendChild(', childId, ')']);
        }

        endWith();

    }

    return id;

}

function handleStyle(valStr) {

    valStr = trim(valStr);
    valStr = valStr.replace(/;$/,'');

    valPairs = valStr.split(';');

    if (valPairs.length == 0) {
        return;
    }
    else if (valPairs.length == 1) {

        var parsed = handleStyle_Parse(valPairs[0]);

        if (parsed != null)
            assignment(['style.', parsed.name], q(parsed.value));

    }
    else {

        startWith('style');

        for each (pair in valPairs) {

            var parsed = handleStyle_Parse(pair);

            if (parsed != null)
                assignment(parsed.name, q(parsed.value));

        }

        endWith();

    }

}

function handleStyle_Parse(valPairStr) {

    valPairStr = trim(valPairStr);
    if (valPairStr.length == 0)
        return null;

    var parts = valPairStr.split(':');

    var name = trim(parts[0]).toLowerCase();

    nameParts = name.split('-');
    if (nameParts.length > 1) {
        name = nameParts[0];
        for (var i = 1; i < nameParts.length; i += 1) {
            var cur = nameParts[i];
            if (cur.length > 0) {
                name += cur[0].toUpperCase();
                name += cur.substring(1);
            }
        }
    }

    if (name == 'float')
        name = 'cssFloat';

    var value = trim(parts[1]);

    return {name: name, value: value};

}

function trim(str) {
    return str.replace(/^\s*/g,'').replace(/\s*$/g,'');
}

function q(str) {

    if (!str || typeof str.indexOf != 'function')
        return '';

    str = str.replace(/\n/g, '\\n');
    str = str.replace(/\r/g, '\\r');

    if (str.indexOf("'") == -1)
        return "'" + str + "'";

    if (str.indexOf('"') == -1)
        return '"' + str + '"';

    return str.replace(/'/g, "\\'");

}
